package com.nth.android.ar;

import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.List;

import android.content.Context;
import android.content.res.Configuration;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.SurfaceHolder.Callback;
import android.view.View.OnTouchListener;

public class ARCameraView extends SurfaceView implements Callback, AutoFocusCallback, OnTouchListener {

    SurfaceHolder holder;
    Camera camera;

    public ARCameraView(Context context, AttributeSet attrs, int defStyle) {
	super(context, attrs, defStyle);
	init();
    }

    public ARCameraView(Context context, AttributeSet attrs) {
	super(context, attrs);
	init();
    }

    public ARCameraView(Context context) {
	super(context);
	init();
    }

    private void init() {
	try {
	    holder = getHolder();
	    holder.addCallback(this);
	    holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
	} catch (Exception ex) {
	}

	setOnTouchListener(this);
    }

    @SuppressWarnings("unchecked")
    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
	try {
	    Camera.Parameters parameters = camera.getParameters();
	    try {
		Method method = Camera.Parameters.class.getMethod("getSupportedPreviewSizes", new Class[] {});

		List<Camera.Size> supportedSizes = null;
		Object retObj = method.invoke(parameters);
		if (retObj != null && retObj instanceof List<?>) {
		    supportedSizes = (List<Camera.Size>) retObj;

		    // preview form factor
		    float ff = (float) w / h;

		    // holder for the best form factor and size
		    float bff = 0;
		    int bestw = 0;
		    int besth = 0;
		    Iterator<Camera.Size> itr = supportedSizes.iterator();

		    // we look for the best preview size, it has to be the
		    // closest to the screen form factor, and be less wide than
		    // the screen itself
		    // the latter requirement is because the HTC Hero with
		    // update 2.1 will report camera preview sizes larger than
		    // the screen, and it will fail to initialize the camera
		    // other devices could work with previews larger than the
		    // screen though
		    while (itr.hasNext()) {
			Camera.Size element = itr.next();
			// current form factor
			float cff = (float) element.width / element.height;
			// check if the current element is a candidate to
			// replace the best match so far
			// current form factor should be closer to the bff
			// preview width should be less than screen width
			// preview width should be more than current bestw
			// this combination will ensure that the highest
			// resolution will win
			if ((ff - cff <= ff - bff) && (element.width <= w) && (element.width >= bestw)) {
			    bff = cff;
			    bestw = element.width;
			    besth = element.height;
			}

		    }
		    if (bestw == 0 || besth == 0) {
			parameters.setPreviewSize(getWidth(), getHeight());
		    } else {
			parameters.setPreviewSize(bestw, besth);
		    }
		} else {
		    parameters.setPreviewSize(480, 320);
		}
	    } catch (Exception ex) {
		parameters.setPreviewSize(480, 320);
	    }

	    int orientation = getContext().getResources().getConfiguration().orientation;
	    if (orientation == Configuration.ORIENTATION_PORTRAIT) {
		parameters.set("orientation", "portrait");
		try {
		    Method method = Camera.class.getMethod("setDisplayOrientation", new Class[] { int.class });
		    method.invoke(camera, 90);
		} catch (Exception e) {
		    e.toString();
		}
	    }
	    camera.setParameters(parameters);
	    camera.startPreview();

	    camera.autoFocus(this);
	} catch (Exception ex) {
	    ex.printStackTrace();
	}
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
	try {
	    if (camera != null) {
		try {
		    camera.stopPreview();
		} catch (Exception ex) {
		}
		try {
		    camera.release();
		} catch (Exception ex) {
		}
		camera = null;
	    }

	    camera = Camera.open();
	    camera.setPreviewDisplay(holder);
	} catch (Exception ex) {
	    if (camera != null) {
		try {
		    camera.stopPreview();
		} catch (Exception ignore) {
		}
		try {
		    camera.release();
		} catch (Exception ignore) {
		}
	    }
	}
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
	if (camera != null) {
	    try {
		camera.stopPreview();
	    } catch (Exception ignore) {
	    }
	    try {
		camera.release();
	    } catch (Exception ignore) {
	    }
	}
    }

    @Override
    public void onAutoFocus(boolean success, Camera camera) {
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
	if (event.getAction() == MotionEvent.ACTION_DOWN) {
	    try {
		camera.autoFocus(this);
	    } catch (Exception e) {
	    }
	    return true;
	}
	return false;
    }

    public Camera getCamera() {
	return camera;
    }

    public void setCamera(Camera camera) {
	this.camera = camera;
    }

}
